﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using GMUtilitiesDotNet.Events;
using GMLogging;

namespace GMUtilitiesDotNet.Threading
{
    public sealed class CustomBackgroundWorker<T, U>
    {
        #region Properties
        private DispatcherObject mDispatcherObject;
        private IWorker<T, U> mWorker;

        private volatile bool mWorking;
        private volatile bool mSkippedWork;
        private Func<T, U> mWorkFunction;

        private T Parameters;
        #endregion

        #region Events
        public event EventHandler<ParameterizedEventArgs<U>> WorkComplete;
        private void RaiseWorkComplete(U workerReturnValue)
        {
            var handler = WorkComplete;
            if (handler != null)
            {
                handler(this, new ParameterizedEventArgs<U>(workerReturnValue));
            }
        }
        #endregion

        #region Constructors
        public CustomBackgroundWorker(DispatcherObject dispatcherObject, IWorker<T, U> worker)
        {
            mDispatcherObject = dispatcherObject;
            mWorker = worker;

            mWorking = false;
            mSkippedWork = false;
            mWorkFunction = new Func<T, U>(mWorker.Work);

            Parameters = default(T);
        }
        #endregion

        #region Methods
        public void Work(T parameters)
        {
            Parameters = parameters;
            if (mWorking)
            {
                mSkippedWork = true;
            }
            else
            {
                mWorking = true;
                mWorkFunction.BeginInvoke(parameters, new AsyncCallback(OnEndWork), null);
            }
        }

        private void OnEndWork(IAsyncResult asyncResult)
        {
            U workerReturnValue = default(U);
            try
            {
                workerReturnValue = mWorkFunction.EndInvoke(asyncResult);
                mDispatcherObject.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<U>(RaiseWorkComplete), workerReturnValue);
            }
            catch (Exception e)
            {
                Logger.Error(e.Message);
            }
            finally
            {
                mWorking = false;
            }

            if (mSkippedWork)
            {
                mSkippedWork = false;
                mDispatcherObject.Dispatcher.Invoke(DispatcherPriority.Normal, new Action<T>(Work), Parameters);
            }
        }
        #endregion
    }
}